/*
 * Copyright 2010-2012 VMware and contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

package cn.wenhao.javaClassReload.javaClassElements;

import java.lang.reflect.Method;

/**
 * 
 * @say little Boy, don't be sad.
 * @name Rezar
 * @time Oct 27, 2016
 * @Desc Representation of a Method.
 */
public class MethodMember extends AbstractMember {
    
    
 // computed up front:
    public final static int BIT_CATCHER = 0x001;

    public final static int BIT_CLASH = 0x0002;

    // identifies a catcher method placed into an abstract class (where a method from a super interface hasn't been implemented)
    public final static int BIT_CATCHER_INTERFACE = 0x004;

    public final static int BIT_SUPERDISPATCHER = 0x0008;

    // computed on incremental members to indicate what changed:
    public final static int MADE_STATIC = 0x0010;

    public final static int MADE_NON_STATIC = 0x0020;

    public final static int VISIBILITY_CHANGE = 0x0040;

    public final static int IS_NEW = 0x0080;

    public final static int WAS_DELETED = 0x0100;

    public final static int EXCEPTIONS_CHANGE = 0x0200;

    public static final MethodMember[] NONE = null;

    protected final String[] exceptions;

    // For MethodMembers in an incremental type descriptor, this tracks the method in the original type descriptor (if
    // there was one)
    public MethodMember original;

    public final String nameAndDescriptor;

    public Method cachedMethod;

    public int bits;

    protected MethodMember(int modifiers, String name, String descriptor, String signature, String[] exceptions) {
        super(modifiers, name, descriptor, signature);
        this.exceptions = perhapsSortIfNecessary(exceptions);
        this.nameAndDescriptor = new StringBuilder(name).append(descriptor).toString();
    }

    private String[] perhapsSortIfNecessary(String[] exceptions) {
        // Arrays.sort(exceptions);
        return exceptions;
    }

    public String[] getExceptions() {
        return exceptions;
    }

    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("0x").append(Integer.toHexString(modifiers));
        sb.append(" ").append(name).append(descriptor);
        if (exceptions.length != 0) {
            sb.append(" throws ");
            for (String ex : exceptions) {
                sb.append(ex).append(" ");
            }
        }
        return sb.toString().trim();
    }

    public String getParamDescriptor() {
        // more likely to be at the end, lets go back from there
        for (int pos = descriptor.length() - 1; pos > 0; pos--) {
            if (descriptor.charAt(pos) == ')') {
                return descriptor.substring(0, pos + 1);
            }
        }
        throw new IllegalStateException("Method has invalid descriptor: " + descriptor);
    }

    public boolean hasReturnValue() {
        return descriptor.charAt(descriptor.length() - 1) != 'V';
    }

    public boolean equals(Object other) {
        if (!(other instanceof MethodMember)) {
            return false;
        }
        MethodMember o = (MethodMember) other;
        if (!name.equals(o.name)) {
            return false;
        }
        if (modifiers != o.modifiers) {
            return false;
        }
        if (!descriptor.equals(o.descriptor)) {
            return false;
        }
        if (exceptions.length != o.exceptions.length) {
            return false;
        }
        if (signature == null && o.signature != null) {
            return false;
        }
        if (signature != null && o.signature == null) {
            return false;
        }
        if (signature != null) {
            if (!signature.equals(o.signature)) {
                return false;
            }
        }
        for (int i = 0; i < exceptions.length; i++) {
            if (!exceptions[i].equals(o.exceptions[i])) {
                return false;
            }
        }
        return true;
    }

    public int hashCode() {
        int result = modifiers;
        result = result * 37 + name.hashCode();
        result = result * 37 + descriptor.hashCode();
        if (signature != null) {
            result = result * 37 + signature.hashCode();
        }
        if (exceptions != null) {
            for (String ex : exceptions) {
                result = result * 37 + ex.hashCode();
            }
        }
        return result;
    }

    /*
     * Determine whether this method should replace the other method on reload. In accordance to how JVM works at class
     * load time, this will be the case if this and other have the same Class, name, parameter types and return type.
     * I.e. formally, in JVM bytecode (unlike source code) a method doesn't override a method with a different return
     * type. When such a situation occurs in source code, the compiler will introduce a bridge method in bytecode.
     */
    public boolean shouldReplace(MethodMember other) {
        if (!name.equals(other.name)) {
            return false;
        }
        if (!descriptor.equals(other.descriptor)) {
            return false;
        }
        return true;
    }

    public boolean isConstructor() {
        return name.equals("<init>");
    }

    /**
     * @return
     */
    public String getNameAndDescriptor() {
        return this.nameAndDescriptor;
    }

    /**
     * @param existingMethod
     * @return
     */
        
    public static boolean isCatcher(MethodMember existingMethod) {
        
        // TODO Auto-generated method stub
        return false;
            
    }

    /**
     * @return
     */
        
    public Object bitsToString() {
        
        // TODO Auto-generated method stub
        return null;
            
    }

    /**
     * @return
     */
        
    public String getGenericSignature() {
        
        // TODO Auto-generated method stub
        return null;
            
    }

}
